library(tidyverse) # for data cleaning and plotting
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5 ✓ purrr 0.3.4
## ✓ tibble 3.1.6 ✓ dplyr 1.0.8
## ✓ tidyr 1.2.0 ✓ stringr 1.4.0
## ✓ readr 2.1.2 ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(gardenR) # for Lisa's garden data
library(lubridate) # for date manipulation
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(openintro) # for the abbr2state() function
## Loading required package: airports
## Loading required package: cherryblossom
## Loading required package: usdata
library(palmerpenguins)# for Palmer penguin data
library(maps) # for map data
##
## Attaching package: 'maps'
## The following object is masked from 'package:purrr':
##
## map
library(ggmap) # for mapping points on maps
## Google's Terms of Service: https://cloud.google.com/maps-platform/terms/.
## Please cite ggmap if you use it! See citation("ggmap") for details.
library(gplots) # for col2hex() function
##
## Attaching package: 'gplots'
## The following object is masked from 'package:stats':
##
## lowess
library(RColorBrewer) # for color palettes
library(sf) # for working with spatial data
## Linking to GEOS 3.9.1, GDAL 3.4.0, PROJ 8.1.1; sf_use_s2() is TRUE
library(leaflet) # for highly customizable mapping
library(ggthemes) # for more themes (including theme_map())
library(plotly) # for the ggplotly() - basic interactivity
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggmap':
##
## wind
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(gganimate) # for adding animation layers to ggplots
library(transformr) # for "tweening" (gganimate)
##
## Attaching package: 'transformr'
## The following object is masked from 'package:sf':
##
## st_normalize
library(gifski) # need the library for creating gifs but don't need to load each time
library(shiny) # for creating interactive apps
theme_set(theme_minimal())
library(babynames)
##
## Attaching package: 'babynames'
## The following object is masked from 'package:openintro':
##
## births
# SNCF Train data
small_trains <- read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2019/2019-02-26/small_trains.csv")
## Rows: 32772 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): service, departure_station, arrival_station, delay_cause
## dbl (9): year, month, journey_time_avg, total_num_trips, avg_delay_all_depar...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Lisa's garden data
data("garden_harvest")
# Lisa's Mallorca cycling data
mallorca_bike_day7 <- read_csv("https://www.dropbox.com/s/zc6jan4ltmjtvy0/mallorca_bike_day7.csv?dl=1") %>%
select(1:4, speed)
## Rows: 3210 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (8): lon, lat, ele, extensions, ele.num, time_hr, dist_km, speed
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Heather Lendway's Ironman 70.3 Pan Am championships Panama data
panama_swim <- read_csv("https://raw.githubusercontent.com/llendway/gps-data/master/data/panama_swim_20160131.csv")
## Rows: 36 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): event
## dbl (3): lon, lat, extensions
## lgl (1): ele
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
panama_bike <- read_csv("https://raw.githubusercontent.com/llendway/gps-data/master/data/panama_bike_20160131.csv")
## Rows: 7924 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): event
## dbl (4): lon, lat, ele, extensions
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
panama_run <- read_csv("https://raw.githubusercontent.com/llendway/gps-data/master/data/panama_run_20160131.csv")
## Rows: 1111 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): event
## dbl (4): lon, lat, ele, extensions
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#COVID-19 data from the New York Times
covid19 <- read_csv("https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv")
## Rows: 41894 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): state, fips
## dbl (2): cases, deaths
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Warm-up exercises from tutorial
- Choose 2 graphs you have created for ANY assignment in this class
and add interactivity using the
ggplotly() function.
```{r, fig.alt= “barplot showing the popularity of the name Emanuel
over time.There is a constant increase starting in 1979 and in 2007 the
popularity started to decrease}” emanuel_popularity <- babynames
%>% filter(sex == “M”) %>% filter(name %in% c (“Emanuel”)) %>%
group_by( name, year) %>% arrange(desc(n)) %>% ungroup() %>%
ggplot(aes( x = year, y = n))+ geom_col(color = “darkblue”)+ labs(title
= “Popularity of the name ‘Emanuel’ over time”, x = ““, y =”“, caption
=”Data from the US and New Zealand, Source: USA social security
administration “)+ theme_clean()
ggplotly(emanuel_popularity, tooltip = c(“text”, “x”))
tomatoes_2020 <-garden_harvest %>%
filter( vegetable == "tomatoes") %>%
group_by(date) %>%
mutate(weight_lbs = weight*0.00220462) %>%
summarise(total_weight_lbs = sum(weight_lbs)) %>%
ggplot(aes(y = total_weight_lbs, x = date, color = variety))+
geom_line(color = "darkblue")+
labs( x= "",
y = "",
title = "The amount of tomatoes harvested by day in pounds during 2020",
caption = "Graph by Emanuel Deleon Otazu") +
theme(plot.title = element_text(hjust = 0.5))+
theme_clean()
ggplotly(tomatoes_2020,
tooltip = c("text", "x"))
- Use animation to tell an interesting story with the
small_trains dataset that contains data from the SNCF
(National Society of French Railways). These are Tidy Tuesday data! Read
more about it here.
#total number of international trips over time #filter only to
international services #total number of trips
small_trains %>%
filter(service == "International") %>%
group_by(year) %>%
summarise(total_trips_year = sum(total_num_trips)) %>%
ggplot(aes(x = year, y = total_trips_year, color = year))+
geom_line()+
labs(title = "Quantity of International trips by year",
subtitle = "year: {frame_along}",
x = "",
y = "",
color = "year") +
transition_reveal(year)

anim_save("small_trains.gif")
knitr::include_graphics("small_trains.gif")

Garden data
- In this exercise, you will create a stacked area plot that reveals
itself over time (see the
geom_area() examples here).
You will look at cumulative harvest of tomato varieties over time. I
have filtered the data to the tomatoes and find the daily
harvest in pounds for each variety. The complete() function
creates a row for all unique date/variety
combinations. If a variety is not harvested on one of the harvest dates
in the dataset, it is filled with a value of 0. You should do the
following:
- For each variety, find the cumulative harvest in pounds.
- Use the data you just made to create a static cumulative harvest
area plot, with the areas filled with different colors for each variety
and arranged (HINT:
fct_reorder()) from most to least
harvested weights (most on the bottom).
- Add animation to reveal the plot over date. Instead of having a
legend, place the variety names directly on the graph (refer back to the
tutorial for how to do this).
garden_harvest %>%
filter(vegetable == "tomatoes") %>%
group_by(date, variety) %>%
summarize(daily_harvest_lb = sum(weight)*0.00220462) %>%
ungroup() %>%
complete(variety,
date,
fill = list(daily_harvest_lb = 0)) %>%
mutate(variety = fct_reorder(variety,daily_harvest_lb, sum)) %>%
group_by(variety) %>%
mutate(cum_variety_harvest =cumsum(daily_harvest_lb)) %>%
ggplot(aes(x = date,
y = cum_variety_harvest ,
fill = (variety)))+
geom_area()+
geom_text (aes(label = variety), position = "stack") +
labs(title = "Cumulative harvest of tomato varieties over time",
subtitle = "Date: {frame_along}",
x = "",
y = "")+
transition_reveal(date)+
theme(legend.position = "none")
## `summarise()` has grouped output by 'date'. You can override using the
## `.groups` argument.

anim_save("harvest_tomato.gif")
knitr::include_graphics("harvest_tomato.gif")

Maps, animation, and movement!
- Map Lisa’s
mallorca_bike_day7 bike ride using
animation! Requirements:
- Plot on a map using
ggmap.
- Show “current” location with a red point.
- Show path up until the current point.
- Color the path according to elevation.
- Show the time in the subtitle.
- CHALLENGE: use the
ggimage package and
geom_image to add a bike image instead of a red point. You
can use this
image. See here
for an example.
- Add something of your own! And comment on if you prefer this to the
static map and why or why not.
mallorca_map <- get_stamenmap(
bbox = c( left = 2.28 , bottom = 39.41 , right = 3.03 , top = 39.8 ),
maptype = "terrain",
zoom = 11)
## Source : http://tile.stamen.com/terrain/11/1036/776.png
## Source : http://tile.stamen.com/terrain/11/1037/776.png
## Source : http://tile.stamen.com/terrain/11/1038/776.png
## Source : http://tile.stamen.com/terrain/11/1039/776.png
## Source : http://tile.stamen.com/terrain/11/1040/776.png
## Source : http://tile.stamen.com/terrain/11/1041/776.png
## Source : http://tile.stamen.com/terrain/11/1036/777.png
## Source : http://tile.stamen.com/terrain/11/1037/777.png
## Source : http://tile.stamen.com/terrain/11/1038/777.png
## Source : http://tile.stamen.com/terrain/11/1039/777.png
## Source : http://tile.stamen.com/terrain/11/1040/777.png
## Source : http://tile.stamen.com/terrain/11/1041/777.png
## Source : http://tile.stamen.com/terrain/11/1036/778.png
## Source : http://tile.stamen.com/terrain/11/1037/778.png
## Source : http://tile.stamen.com/terrain/11/1038/778.png
## Source : http://tile.stamen.com/terrain/11/1039/778.png
## Source : http://tile.stamen.com/terrain/11/1040/778.png
## Source : http://tile.stamen.com/terrain/11/1041/778.png
## Source : http://tile.stamen.com/terrain/11/1036/779.png
## Source : http://tile.stamen.com/terrain/11/1037/779.png
## Source : http://tile.stamen.com/terrain/11/1038/779.png
## Source : http://tile.stamen.com/terrain/11/1039/779.png
## Source : http://tile.stamen.com/terrain/11/1040/779.png
## Source : http://tile.stamen.com/terrain/11/1041/779.png
ggmap(mallorca_map) +
geom_point(data = mallorca_bike_day7,
aes(x = lon, y = lat),
zoom = .5) +
geom_path(data = mallorca_bike_day7 ,
aes(x = lon , y = lat , color = ele ),
size = .3) +
annotate(geom = "point", y = 39.66002, x = 2.586712, color = "darkred", size = 3)+
annotate(geom = "text", y = 39.66002, x = 2.586712, label = "current", size = 2)+
theme(legend.background = element_blank())+
labs(title = "Lisa's Mallorca bike ride",
subtitle = "Time: {frame_along}",
color = "Elevation") +
scale_color_viridis_c(option = "magma" ) +
transition_reveal(time) +
theme_map()
## Warning: Ignoring unknown parameters: zoom

anim_save("lisas_mallorca_bikeride.gif")
knitr::include_graphics("lisas_mallorca_bikeride.gif")

- In this exercise, you get to meet Lisa’s sister, Heather! She is a
proud Mac grad, currently works as a Data Scientist where she uses R
everyday, and for a few years (while still holding a full-time job) she
was a pro triathlete. You are going to map one of her races. The data
from each discipline of the Ironman 70.3 Pan Am championships, Panama is
in a separate file -
panama_swim, panama_bike,
and panama_run. Create a similar map to the one you created
with my cycling data. You will need to make some small changes: 1.
combine the files putting them in swim, bike, run order (HINT:
bind_rows()), 2. make the leading dot a different color
depending on the event (for an extra challenge, make it a different
image using `geom_image()!), 3. CHALLENGE (optional): color by speed,
which you will need to compute on your own from the data. You can read
Heather’s race report here.
She is also in the Macalester Athletics Hall
of Fame and still has records at the pool.
panama <-bind_rows(panama_swim, panama_bike, panama_run)
panama_map <- get_stamenmap(
bbox = c( left = -79.6901, bottom = 8.8590, right = -79.4041 , top = 9.0751),
maptype = "terrain",
zoom = 7)
## Source : http://tile.stamen.com/terrain/7/35/60.png
ggmap(panama_map) +
geom_point( data = panama,
aes(x = lon, y = lat, color = event),
size = 5)+
geom_path(data = panama,
aes(x = lon, y = lat),
size = .8)+
labs(title = "Heather's triathlon race in Panama",
subtitle = "Time: {frame_along}",
color = "event") +
scale_color_viridis_d(option = "inferno" ) +
transition_reveal(time) +
theme_map()

anim_save("heathers_race.gif")
knitr::include_graphics("heathers_race.gif")

COVID-19 data
- In this exercise you will animate a map of the US, showing how
cumulative COVID-19 cases per 10,000 residents has changed over time.
This is similar to exercises 11 & 12 from the previous exercises,
with the added animation! So, in the end, you should have something like
the static map you made there, but animated over all the days. The code
below gives the population estimates for each state and loads the
states_map data. Here is a list of details you should
include in the plot:
- Put date in the subtitle.
- Because there are so many dates, you are going to only do the
animation for the the 15th of each month. So, filter only to those dates
- there are some lubridate functions that can help you do this.
- Use the
animate() function to make the animation 200
frames instead of the default 100 and to pause for 10 frames on the end
frame.
- Use
group = date in aes().
- Comment on what you see.
census_pop_est_2018 <- read_csv("https://www.dropbox.com/s/6txwv3b4ng7pepe/us_census_2018_state_pop_est.csv?dl=1") %>%
separate(state, into = c("dot","state"), extra = "merge") %>%
select(-dot) %>%
mutate(state = str_to_lower(state))
## Rows: 51 Columns: 2
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): state
## dbl (1): est_pop_2018
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
states_map <- map_data("state")
newer_cases <- covid19 %>%
arrange(desc(date)) %>%
group_by(state) %>%
mutate(numberrow = 1:n()) %>%
mutate(state = str_to_lower(`state`))
covid_population <- newer_cases %>%
left_join( census_pop_est_2018,
by = c("state"="state")) %>%
mutate(covid_per_10000 = (cases/est_pop_2018)*10000)
covid_population %>%
filter(day(date) == 15) %>%
ggplot()+
geom_map(map = states_map,
aes(map_id = state,
fill = covid_per_10000,
group = date))+
labs(title = "COVID-19 cases per 10,000 residents by date in the US",
subtitle = "Date: {closest_state}",
fill = "Cases per 10,000",
caption = "")+
expand_limits( x = states_map$long, y = states_map$lat)+
scale_fill_viridis_c( option = "magma", direction = -1)+
theme_map()+
theme(legend.background = element_blank())+
transition_states(date, transition_length = 0)

anim_save("covid_cases.gif")
knitr::include_graphics("covid_cases.gif")

Your first shiny app (for next week!)
- This app will also use the COVID data. Make sure you load that data
and all the libraries you need in the
app.R file you
create. You should create a new project for the app, separate from the
homework project. Below, you will post a link to the app that you
publish on shinyapps.io. You will create an app to compare states’ daily
number of COVID cases per 100,000 over time. The x-axis will be date.
You will have an input box where the user can choose which states to
compare (selectInput()), a slider where the user can choose
the date range, and a submit button to click once the user has chosen
all states they’re interested in comparing. The graph should display a
different line for each state, with labels either on the graph or in a
legend. Color can be used if needed.
Put the link to your app here: https://edeleono.shinyapps.io/covid-app/
LS0tCnRpdGxlOiAnV2Vla2x5IEV4ZXJjaXNlcyAjNScKYXV0aG9yOiAiRW1hbnVlbCBEZWxlb24gT3RhenUiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGtlZXBfbWQ6IFRSVUUKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiNrbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGVycm9yPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICMgZm9yIGRhdGEgY2xlYW5pbmcgYW5kIHBsb3R0aW5nCmxpYnJhcnkoZ2FyZGVuUikgICAgICAgIyBmb3IgTGlzYSdzIGdhcmRlbiBkYXRhCmxpYnJhcnkobHVicmlkYXRlKSAgICAgIyBmb3IgZGF0ZSBtYW5pcHVsYXRpb24KbGlicmFyeShvcGVuaW50cm8pICAgICAjIGZvciB0aGUgYWJicjJzdGF0ZSgpIGZ1bmN0aW9uCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpIyBmb3IgUGFsbWVyIHBlbmd1aW4gZGF0YQpsaWJyYXJ5KG1hcHMpICAgICAgICAgICMgZm9yIG1hcCBkYXRhCmxpYnJhcnkoZ2dtYXApICAgICAgICAgIyBmb3IgbWFwcGluZyBwb2ludHMgb24gbWFwcwpsaWJyYXJ5KGdwbG90cykgICAgICAgICMgZm9yIGNvbDJoZXgoKSBmdW5jdGlvbgpsaWJyYXJ5KFJDb2xvckJyZXdlcikgICMgZm9yIGNvbG9yIHBhbGV0dGVzCmxpYnJhcnkoc2YpICAgICAgICAgICAgIyBmb3Igd29ya2luZyB3aXRoIHNwYXRpYWwgZGF0YQpsaWJyYXJ5KGxlYWZsZXQpICAgICAgICMgZm9yIGhpZ2hseSBjdXN0b21pemFibGUgbWFwcGluZwpsaWJyYXJ5KGdndGhlbWVzKSAgICAgICMgZm9yIG1vcmUgdGhlbWVzIChpbmNsdWRpbmcgdGhlbWVfbWFwKCkpCmxpYnJhcnkocGxvdGx5KSAgICAgICAgIyBmb3IgdGhlIGdncGxvdGx5KCkgLSBiYXNpYyBpbnRlcmFjdGl2aXR5CmxpYnJhcnkoZ2dhbmltYXRlKSAgICAgIyBmb3IgYWRkaW5nIGFuaW1hdGlvbiBsYXllcnMgdG8gZ2dwbG90cwpsaWJyYXJ5KHRyYW5zZm9ybXIpICAgICMgZm9yICJ0d2VlbmluZyIgKGdnYW5pbWF0ZSkKbGlicmFyeShnaWZza2kpICAgICAgICAjIG5lZWQgdGhlIGxpYnJhcnkgZm9yIGNyZWF0aW5nIGdpZnMgYnV0IGRvbid0IG5lZWQgdG8gbG9hZCBlYWNoIHRpbWUKbGlicmFyeShzaGlueSkgICAgICAgICAjIGZvciBjcmVhdGluZyBpbnRlcmFjdGl2ZSBhcHBzCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCmxpYnJhcnkoYmFieW5hbWVzKQpgYGAKCmBgYHtyIGRhdGF9CiMgU05DRiBUcmFpbiBkYXRhCnNtYWxsX3RyYWlucyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDE5LzIwMTktMDItMjYvc21hbGxfdHJhaW5zLmNzdiIpIAoKIyBMaXNhJ3MgZ2FyZGVuIGRhdGEKZGF0YSgiZ2FyZGVuX2hhcnZlc3QiKQoKIyBMaXNhJ3MgTWFsbG9yY2EgY3ljbGluZyBkYXRhCm1hbGxvcmNhX2Jpa2VfZGF5NyA8LSByZWFkX2NzdigiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy96YzZqYW40bHRtanR2eTAvbWFsbG9yY2FfYmlrZV9kYXk3LmNzdj9kbD0xIikgJT4lIAogIHNlbGVjdCgxOjQsIHNwZWVkKQoKIyBIZWF0aGVyIExlbmR3YXkncyBJcm9ubWFuIDcwLjMgUGFuIEFtIGNoYW1waW9uc2hpcHMgUGFuYW1hIGRhdGEKcGFuYW1hX3N3aW0gPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sbGVuZHdheS9ncHMtZGF0YS9tYXN0ZXIvZGF0YS9wYW5hbWFfc3dpbV8yMDE2MDEzMS5jc3YiKQoKcGFuYW1hX2Jpa2UgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sbGVuZHdheS9ncHMtZGF0YS9tYXN0ZXIvZGF0YS9wYW5hbWFfYmlrZV8yMDE2MDEzMS5jc3YiKQoKcGFuYW1hX3J1biA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xsZW5kd2F5L2dwcy1kYXRhL21hc3Rlci9kYXRhL3BhbmFtYV9ydW5fMjAxNjAxMzEuY3N2IikKCiNDT1ZJRC0xOSBkYXRhIGZyb20gdGhlIE5ldyBZb3JrIFRpbWVzCmNvdmlkMTkgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ueXRpbWVzL2NvdmlkLTE5LWRhdGEvbWFzdGVyL3VzLXN0YXRlcy5jc3YiKQoKYGBgCgoKCiMjIFdhcm0tdXAgZXhlcmNpc2VzIGZyb20gdHV0b3JpYWwKCiAgMS4gQ2hvb3NlIDIgZ3JhcGhzIHlvdSBoYXZlIGNyZWF0ZWQgZm9yIEFOWSBhc3NpZ25tZW50IGluIHRoaXMgY2xhc3MgYW5kIGFkZCBpbnRlcmFjdGl2aXR5IHVzaW5nIHRoZSBgZ2dwbG90bHkoKWAgZnVuY3Rpb24uCiAgCmBgYHtyLCBmaWcuYWx0PSAiYmFycGxvdCBzaG93aW5nIHRoZSBwb3B1bGFyaXR5IG9mIHRoZSBuYW1lIEVtYW51ZWwgb3ZlciB0aW1lLlRoZXJlIGlzIGEgY29uc3RhbnQgaW5jcmVhc2Ugc3RhcnRpbmcgaW4gMTk3OSBhbmQgaW4gMjAwNyB0aGUgcG9wdWxhcml0eSBzdGFydGVkIHRvIGRlY3JlYXNlfSIKZW1hbnVlbF9wb3B1bGFyaXR5IDwtIGJhYnluYW1lcyAlPiUgCiAgZmlsdGVyKHNleCA9PSAiTSIpICU+JSAKICBmaWx0ZXIobmFtZSAlaW4lIGMgKCJFbWFudWVsIikpICU+JSAKICBncm91cF9ieSggbmFtZSwgeWVhcikgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCmdncGxvdChhZXMoIHggPSB5ZWFyLAogICAgICAgICAgICB5ID0gbikpKwpnZW9tX2NvbChjb2xvciA9ICJkYXJrYmx1ZSIpKwogIGxhYnModGl0bGUgPSAiUG9wdWxhcml0eSBvZiB0aGUgbmFtZSAnRW1hbnVlbCcgb3ZlciB0aW1lICIsCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIsCiAgICAgICBjYXB0aW9uID0iRGF0YSBmcm9tIHRoZSBVUyBhbmQgTmV3IFplYWxhbmQsCiAgICAgICAgICAgICAgICBTb3VyY2U6IFVTQSBzb2NpYWwgc2VjdXJpdHkgYWRtaW5pc3RyYXRpb24gIikrCiAgdGhlbWVfY2xlYW4oKQoKZ2dwbG90bHkoZW1hbnVlbF9wb3B1bGFyaXR5LAogICAgICAgICB0b29sdGlwID0gYygidGV4dCIsICJ4IikpIAoKICAKYGBge3IsIGZpZy5hbHQ9ImxpbmUgZ3JhcGggc2hvd2luZyB0aGUgVGhlIGFtb3VudCBvZiB0b21hdG9lcyBoYXJ2ZXN0ZWQgYnkgZGF5IGluIHBvdW5kcyBkdXJpbmcgMjAyMCIgfQp0b21hdG9lc18yMDIwIDwtZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGZpbHRlciggdmVnZXRhYmxlID09ICJ0b21hdG9lcyIpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgbXV0YXRlKHdlaWdodF9sYnMgPSB3ZWlnaHQqMC4wMDIyMDQ2MikgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF93ZWlnaHRfbGJzID0gc3VtKHdlaWdodF9sYnMpKSAlPiUgIApnZ3Bsb3QoYWVzKHkgPSB0b3RhbF93ZWlnaHRfbGJzLCB4ID0gZGF0ZSwgY29sb3IgPSB2YXJpZXR5KSkrCiAgZ2VvbV9saW5lKGNvbG9yID0gImRhcmtibHVlIikrCiAgbGFicyggeD0gIiIsCiAgICAgICAgeSA9ICAiIiwKICAgICAgICB0aXRsZSA9ICJUaGUgYW1vdW50IG9mIHRvbWF0b2VzIGhhcnZlc3RlZCBieSBkYXkgaW4gcG91bmRzIGR1cmluZyAyMDIwIiwKICAgICAgIGNhcHRpb24gPSAiR3JhcGggYnkgRW1hbnVlbCBEZWxlb24gT3RhenUiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpKwogIHRoZW1lX2NsZWFuKCkKCmdncGxvdGx5KHRvbWF0b2VzXzIwMjAsIAogICAgICAgICB0b29sdGlwID0gYygidGV4dCIsICJ4IikpCmBgYAoKICAKICAyLiBVc2UgYW5pbWF0aW9uIHRvIHRlbGwgYW4gaW50ZXJlc3Rpbmcgc3Rvcnkgd2l0aCB0aGUgYHNtYWxsX3RyYWluc2AgZGF0YXNldCB0aGF0IGNvbnRhaW5zIGRhdGEgZnJvbSB0aGUgU05DRiAoTmF0aW9uYWwgU29jaWV0eSBvZiBGcmVuY2ggUmFpbHdheXMpLiBUaGVzZSBhcmUgVGlkeSBUdWVzZGF5IGRhdGEhIFJlYWQgbW9yZSBhYm91dCBpdCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS90cmVlL21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wMi0yNikuCiAgCiAgI3RvdGFsIG51bWJlciBvZiBpbnRlcm5hdGlvbmFsIHRyaXBzIG92ZXIgdGltZSAKICAjZmlsdGVyIG9ubHkgdG8gaW50ZXJuYXRpb25hbCBzZXJ2aWNlcyAKICAjdG90YWwgbnVtYmVyIG9mIHRyaXBzIAoKYGBge3IsIGZpZy5hbHQ9IkFuaW1hdGVkIGxpbmUgZ3JhcGggc2hvd2luZyB0aGUgcXVhbnRpdHkgb2YgaW50ZXJuYXRpb25hbCB0cmlwcyBieSB5ZWFyLiBUaGVyZSBpcyBhIGRlY3JlYXNlIGluIHRoZSBxdWFudGl0eSBzdGFydGluZyBpbiAyMDE1IGFuZCBpbiAyMDE2IGluIHN0YXJ0cyB0byBpbmNyZWFzZSBhZ2FpbiJ9CnNtYWxsX3RyYWlucyAlPiUgCiAgZmlsdGVyKHNlcnZpY2UgPT0gIkludGVybmF0aW9uYWwiKSAlPiUgCiAgZ3JvdXBfYnkoeWVhcikgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF90cmlwc195ZWFyID0gc3VtKHRvdGFsX251bV90cmlwcykpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gdG90YWxfdHJpcHNfeWVhciwgY29sb3IgPSB5ZWFyKSkrCiAgZ2VvbV9saW5lKCkrCiAgbGFicyh0aXRsZSA9ICJRdWFudGl0eSBvZiBJbnRlcm5hdGlvbmFsIHRyaXBzIGJ5IHllYXIiLAogICAgICAgc3VidGl0bGUgPSAieWVhcjoge2ZyYW1lX2Fsb25nfSIsCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIsCiAgICAgICBjb2xvciA9ICJ5ZWFyIikgKwogIHRyYW5zaXRpb25fcmV2ZWFsKHllYXIpCgoKYW5pbV9zYXZlKCJzbWFsbF90cmFpbnMuZ2lmIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInNtYWxsX3RyYWlucy5naWYiKQogIApgYGAKCgojIyBHYXJkZW4gZGF0YQoKICAzLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjcmVhdGUgYSBzdGFja2VkIGFyZWEgcGxvdCB0aGF0IHJldmVhbHMgaXRzZWxmIG92ZXIgdGltZSAoc2VlIHRoZSBgZ2VvbV9hcmVhKClgIGV4YW1wbGVzIFtoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvcG9zaXRpb25fc3RhY2suaHRtbCkpLiBZb3Ugd2lsbCBsb29rIGF0IGN1bXVsYXRpdmUgaGFydmVzdCBvZiB0b21hdG8gdmFyaWV0aWVzIG92ZXIgdGltZS4gSSBoYXZlIGZpbHRlcmVkIHRoZSBkYXRhIHRvIHRoZSB0b21hdG9lcyBhbmQgZmluZCB0aGUgKmRhaWx5KiBoYXJ2ZXN0IGluIHBvdW5kcyBmb3IgZWFjaCB2YXJpZXR5LiBUaGUgYGNvbXBsZXRlKClgIGZ1bmN0aW9uIGNyZWF0ZXMgYSByb3cgZm9yIGFsbCB1bmlxdWUgYGRhdGVgL2B2YXJpZXR5YCBjb21iaW5hdGlvbnMuIElmIGEgdmFyaWV0eSBpcyBub3QgaGFydmVzdGVkIG9uIG9uZSBvZiB0aGUgaGFydmVzdCBkYXRlcyBpbiB0aGUgZGF0YXNldCwgaXQgaXMgZmlsbGVkIHdpdGggYSB2YWx1ZSBvZiAwLiAKICBZb3Ugc2hvdWxkIGRvIHRoZSBmb2xsb3dpbmc6CiAgKiBGb3IgZWFjaCB2YXJpZXR5LCBmaW5kIHRoZSBjdW11bGF0aXZlIGhhcnZlc3QgaW4gcG91bmRzLiAgCiAgKiBVc2UgdGhlIGRhdGEgeW91IGp1c3QgbWFkZSB0byBjcmVhdGUgYSBzdGF0aWMgY3VtdWxhdGl2ZSBoYXJ2ZXN0IGFyZWEgcGxvdCwgd2l0aCB0aGUgYXJlYXMgZmlsbGVkIHdpdGggZGlmZmVyZW50IGNvbG9ycyBmb3IgZWFjaCB2YXJpZXR5IGFuZCBhcnJhbmdlZCAoSElOVDogYGZjdF9yZW9yZGVyKClgKSBmcm9tIG1vc3QgdG8gbGVhc3QgaGFydmVzdGVkIHdlaWdodHMgKG1vc3Qgb24gdGhlIGJvdHRvbSkuICAKICAqIEFkZCBhbmltYXRpb24gdG8gcmV2ZWFsIHRoZSBwbG90IG92ZXIgZGF0ZS4gSW5zdGVhZCBvZiBoYXZpbmcgYSBsZWdlbmQsIHBsYWNlIHRoZSB2YXJpZXR5IG5hbWVzIGRpcmVjdGx5IG9uIHRoZSBncmFwaCAocmVmZXIgYmFjayB0byB0aGUgdHV0b3JpYWwgZm9yIGhvdyB0byBkbyB0aGlzKS4KCmBgYHtyLCBmaWcuYWx0PSAic3RhY2tlZCBhcmVhIHBsb3QgdGhhdCBzaG93cyB0aGUgQ3VtdWxhdGl2ZSBoYXJ2ZXN0IG9mIHRvbWF0byB2YXJpZXRpZXMgb3ZlciB0aW1lIn0KZ2FyZGVuX2hhcnZlc3QgJT4lIAogIGZpbHRlcih2ZWdldGFibGUgPT0gInRvbWF0b2VzIikgJT4lIAogIGdyb3VwX2J5KGRhdGUsIHZhcmlldHkpICU+JSAKICBzdW1tYXJpemUoZGFpbHlfaGFydmVzdF9sYiA9IHN1bSh3ZWlnaHQpKjAuMDAyMjA0NjIpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvbXBsZXRlKHZhcmlldHksIAogICAgICAgICAgIGRhdGUsIAogICAgICAgICAgIGZpbGwgPSBsaXN0KGRhaWx5X2hhcnZlc3RfbGIgPSAwKSkgJT4lIAogIG11dGF0ZSh2YXJpZXR5ID0gZmN0X3Jlb3JkZXIodmFyaWV0eSxkYWlseV9oYXJ2ZXN0X2xiLCBzdW0pKSAlPiUgCiAgZ3JvdXBfYnkodmFyaWV0eSkgJT4lIAogIG11dGF0ZShjdW1fdmFyaWV0eV9oYXJ2ZXN0ID1jdW1zdW0oZGFpbHlfaGFydmVzdF9sYikpICAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgCiAgICAgICAgICAgICB5ID0gY3VtX3ZhcmlldHlfaGFydmVzdCAsIAogICAgICAgICAgICAgZmlsbCA9ICh2YXJpZXR5KSkpKwogIGdlb21fYXJlYSgpKwogIGdlb21fdGV4dCAoYWVzKGxhYmVsID0gdmFyaWV0eSksIHBvc2l0aW9uID0gInN0YWNrIikgKwogIGxhYnModGl0bGUgPSAiQ3VtdWxhdGl2ZSBoYXJ2ZXN0IG9mIHRvbWF0byB2YXJpZXRpZXMgb3ZlciB0aW1lIiwKICAgICAgIHN1YnRpdGxlID0gIkRhdGU6IHtmcmFtZV9hbG9uZ30iLAogICAgICAgeCA9ICIiLAogICAgICAgeSA9ICIiKSsKICB0cmFuc2l0aW9uX3JldmVhbChkYXRlKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgphbmltX3NhdmUoImhhcnZlc3RfdG9tYXRvLmdpZiIpCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJoYXJ2ZXN0X3RvbWF0by5naWYiKQogIAogIApgYGAKCgojIyBNYXBzLCBhbmltYXRpb24sIGFuZCBtb3ZlbWVudCEKCiAgNC4gTWFwIExpc2EncyBgbWFsbG9yY2FfYmlrZV9kYXk3YCBiaWtlIHJpZGUgdXNpbmcgYW5pbWF0aW9uISAKICBSZXF1aXJlbWVudHM6CiAgKiBQbG90IG9uIGEgbWFwIHVzaW5nIGBnZ21hcGAuICAKICAqIFNob3cgImN1cnJlbnQiIGxvY2F0aW9uIHdpdGggYSByZWQgcG9pbnQuIAogICogU2hvdyBwYXRoIHVwIHVudGlsIHRoZSBjdXJyZW50IHBvaW50LiAgCiAgKiBDb2xvciB0aGUgcGF0aCBhY2NvcmRpbmcgdG8gZWxldmF0aW9uLiAgCiAgKiBTaG93IHRoZSB0aW1lIGluIHRoZSBzdWJ0aXRsZS4gIAogICogQ0hBTExFTkdFOiB1c2UgdGhlIGBnZ2ltYWdlYCBwYWNrYWdlIGFuZCBgZ2VvbV9pbWFnZWAgdG8gYWRkIGEgYmlrZSBpbWFnZSBpbnN0ZWFkIG9mIGEgcmVkIHBvaW50LiBZb3UgY2FuIHVzZSBbdGhpc10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xsZW5kd2F5L2FuaW1hdGlvbl9hbmRfaW50ZXJhY3Rpdml0eS9tYXN0ZXIvYmlrZS5wbmcpIGltYWdlLiBTZWUgW2hlcmVdKGh0dHBzOi8vZ29vZGVrYXQuZ2l0aHViLmlvL3ByZXNlbnRhdGlvbnMvMjAxOS1pc3VnZy1nZ2FuaW1hdGUtc3Bvb2t5L3NsaWRlcy5odG1sIzM1KSBmb3IgYW4gZXhhbXBsZS4gCiAgKiBBZGQgc29tZXRoaW5nIG9mIHlvdXIgb3duISBBbmQgY29tbWVudCBvbiBpZiB5b3UgcHJlZmVyIHRoaXMgdG8gdGhlIHN0YXRpYyBtYXAgYW5kIHdoeSBvciB3aHkgbm90LgogIApgYGB7ciwgZmlnLmFsdD0gIk1hcCBzaG93aW5nIExpc2EncyBiaWtlIHJpZGUgaW4gTWFsbG9yY2EtIFNwYWluIHdpdGggYW4gYW5pbWF0ZWQgbGluZSBzaG93aW5nIHRoZSB0cmFqZWN0b3J5In0KbWFsbG9yY2FfbWFwIDwtIGdldF9zdGFtZW5tYXAoCiAgICBiYm94ID0gYyggbGVmdCA9IDIuMjggLCBib3R0b20gPSAzOS40MSAsIHJpZ2h0ID0gMy4wMyAsIHRvcCA9IDM5LjggKSwgCiAgICBtYXB0eXBlID0gInRlcnJhaW4iLAogICAgem9vbSA9IDExKQoKZ2dtYXAobWFsbG9yY2FfbWFwKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWFsbG9yY2FfYmlrZV9kYXk3LAogICAgICAgICAgICAgYWVzKHggPSBsb24sIHkgPSBsYXQpLAogICAgICAgICAgICAgem9vbSA9IC41KSArIAogIGdlb21fcGF0aChkYXRhID0gbWFsbG9yY2FfYmlrZV9kYXk3ICwgCiAgICAgICAgICAgICBhZXMoeCA9IGxvbiAgLCB5ID0gbGF0ICAsIGNvbG9yID0gZWxlICksCiAgICAgICAgICAgICBzaXplID0gLjMpICsKICBhbm5vdGF0ZShnZW9tID0gInBvaW50IiwgeSA9IDM5LjY2MDAyLCB4ID0gMi41ODY3MTIsIGNvbG9yID0gImRhcmtyZWQiLCBzaXplID0gMykrCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeSA9IDM5LjY2MDAyLCB4ID0gMi41ODY3MTIsIGxhYmVsID0gImN1cnJlbnQiLCBzaXplID0gMikrCiAgdGhlbWUobGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpKwogIGxhYnModGl0bGUgPSAiTGlzYSdzIE1hbGxvcmNhIGJpa2UgcmlkZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJUaW1lOiB7ZnJhbWVfYWxvbmd9IiwKICAgICAgIGNvbG9yID0gIkVsZXZhdGlvbiIpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIiApICsKICB0cmFuc2l0aW9uX3JldmVhbCh0aW1lKSArCiAgdGhlbWVfbWFwKCkKCmFuaW1fc2F2ZSgibGlzYXNfbWFsbG9yY2FfYmlrZXJpZGUuZ2lmIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImxpc2FzX21hbGxvcmNhX2Jpa2VyaWRlLmdpZiIpCiAgCiAgCmBgYAogIAogIAogIDUuIEluIHRoaXMgZXhlcmNpc2UsIHlvdSBnZXQgdG8gbWVldCBMaXNhJ3Mgc2lzdGVyLCBIZWF0aGVyISBTaGUgaXMgYSBwcm91ZCBNYWMgZ3JhZCwgY3VycmVudGx5IHdvcmtzIGFzIGEgRGF0YSBTY2llbnRpc3Qgd2hlcmUgc2hlIHVzZXMgUiBldmVyeWRheSwgYW5kIGZvciBhIGZldyB5ZWFycyAod2hpbGUgc3RpbGwgaG9sZGluZyBhIGZ1bGwtdGltZSBqb2IpIHNoZSB3YXMgYSBwcm8gdHJpYXRobGV0ZS4gWW91IGFyZSBnb2luZyB0byBtYXAgb25lIG9mIGhlciByYWNlcy4gVGhlIGRhdGEgZnJvbSBlYWNoIGRpc2NpcGxpbmUgb2YgdGhlIElyb25tYW4gNzAuMyBQYW4gQW0gY2hhbXBpb25zaGlwcywgUGFuYW1hIGlzIGluIGEgc2VwYXJhdGUgZmlsZSAtIGBwYW5hbWFfc3dpbWAsIGBwYW5hbWFfYmlrZWAsIGFuZCBgcGFuYW1hX3J1bmAuIENyZWF0ZSBhIHNpbWlsYXIgbWFwIHRvIHRoZSBvbmUgeW91IGNyZWF0ZWQgd2l0aCBteSBjeWNsaW5nIGRhdGEuIFlvdSB3aWxsIG5lZWQgdG8gbWFrZSBzb21lIHNtYWxsIGNoYW5nZXM6IDEuIGNvbWJpbmUgdGhlIGZpbGVzIHB1dHRpbmcgdGhlbSBpbiBzd2ltLCBiaWtlLCBydW4gb3JkZXIgKEhJTlQ6IGBiaW5kX3Jvd3MoKWApLCAyLiBtYWtlIHRoZSBsZWFkaW5nIGRvdCBhIGRpZmZlcmVudCBjb2xvciBkZXBlbmRpbmcgb24gdGhlIGV2ZW50IChmb3IgYW4gZXh0cmEgY2hhbGxlbmdlLCBtYWtlIGl0IGEgZGlmZmVyZW50IGltYWdlIHVzaW5nIGBnZW9tX2ltYWdlKCkhKSwgMy4gQ0hBTExFTkdFIChvcHRpb25hbCk6IGNvbG9yIGJ5IHNwZWVkLCB3aGljaCB5b3Ugd2lsbCBuZWVkIHRvIGNvbXB1dGUgb24geW91ciBvd24gZnJvbSB0aGUgZGF0YS4gWW91IGNhbiByZWFkIEhlYXRoZXIncyByYWNlIHJlcG9ydCBbaGVyZV0oaHR0cHM6Ly9oZWF0aGVybGVuZHdheS5jb20vMjAxNi8wMi8xMC9pcm9ubWFuLTcwLTMtcGFuLWFtZXJpY2FuLWNoYW1waW9uc2hpcHMtcGFuYW1hLXJhY2UtcmVwb3J0LykuIFNoZSBpcyBhbHNvIGluIHRoZSBNYWNhbGVzdGVyIEF0aGxldGljcyBbSGFsbCBvZiBGYW1lXShodHRwczovL2F0aGxldGljcy5tYWNhbGVzdGVyLmVkdS9ob25vcnMvaGFsbC1vZi1mYW1lL2hlYXRoZXItbGVuZHdheS8xODQpIGFuZCBzdGlsbCBoYXMgcmVjb3JkcyBhdCB0aGUgcG9vbC4gCiAgCmBgYHtyfQpwYW5hbWEgPC1iaW5kX3Jvd3MocGFuYW1hX3N3aW0sIHBhbmFtYV9iaWtlLCBwYW5hbWFfcnVuKQoKcGFuYW1hX21hcCA8LSBnZXRfc3RhbWVubWFwKAogICAgYmJveCA9IGMoIGxlZnQgPSAtNzkuNjkwMSwgYm90dG9tID0gOC44NTkwLCByaWdodCA9IC03OS40MDQxICwgdG9wID0gOS4wNzUxKSwgCiAgICBtYXB0eXBlID0gInRlcnJhaW4iLAogICAgem9vbSA9IDcpCgoKIApgYGAKCmBgYHtyfQogZ2dtYXAocGFuYW1hX21hcCkgKwogIGdlb21fcG9pbnQoIGRhdGEgPSBwYW5hbWEsCiAgICAgICAgICAgICAgYWVzKHggPSBsb24sIHkgPSBsYXQsIGNvbG9yID0gZXZlbnQpLAogICAgICAgICAgICAgIHNpemUgPSA1KSsKICBnZW9tX3BhdGgoZGF0YSA9IHBhbmFtYSwKICAgICAgICAgICAgYWVzKHggPSBsb24sIHkgPSBsYXQpLAogICAgICAgICAgICBzaXplID0gLjgpKwogIGxhYnModGl0bGUgPSAiSGVhdGhlcidzIHRyaWF0aGxvbiByYWNlIGluIFBhbmFtYSIsCiAgICAgICBzdWJ0aXRsZSA9ICJUaW1lOiB7ZnJhbWVfYWxvbmd9IiwKICAgICAgIGNvbG9yID0gImV2ZW50IikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiaW5mZXJubyIgKSArCiAgdHJhbnNpdGlvbl9yZXZlYWwodGltZSkgKwogIHRoZW1lX21hcCgpCgphbmltX3NhdmUoImhlYXRoZXJzX3JhY2UuZ2lmIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImhlYXRoZXJzX3JhY2UuZ2lmIikKCmBgYAoKICAKIyMgQ09WSUQtMTkgZGF0YQoKICA2LiBJbiB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIGFuaW1hdGUgYSBtYXAgb2YgdGhlIFVTLCBzaG93aW5nIGhvdyBjdW11bGF0aXZlIENPVklELTE5IGNhc2VzIHBlciAxMCwwMDAgcmVzaWRlbnRzIGhhcyBjaGFuZ2VkIG92ZXIgdGltZS4gVGhpcyBpcyBzaW1pbGFyIHRvIGV4ZXJjaXNlcyAxMSAmIDEyIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlcywgd2l0aCB0aGUgYWRkZWQgYW5pbWF0aW9uISBTbywgaW4gdGhlIGVuZCwgeW91IHNob3VsZCBoYXZlIHNvbWV0aGluZyBsaWtlIHRoZSBzdGF0aWMgbWFwIHlvdSBtYWRlIHRoZXJlLCBidXQgYW5pbWF0ZWQgb3ZlciBhbGwgdGhlIGRheXMuIFRoZSBjb2RlIGJlbG93IGdpdmVzIHRoZSBwb3B1bGF0aW9uIGVzdGltYXRlcyBmb3IgZWFjaCBzdGF0ZSBhbmQgbG9hZHMgdGhlIGBzdGF0ZXNfbWFwYCBkYXRhLiBIZXJlIGlzIGEgbGlzdCBvZiBkZXRhaWxzIHlvdSBzaG91bGQgaW5jbHVkZSBpbiB0aGUgcGxvdDoKICAKICAqIFB1dCBkYXRlIGluIHRoZSBzdWJ0aXRsZS4gICAKICAqIEJlY2F1c2UgdGhlcmUgYXJlIHNvIG1hbnkgZGF0ZXMsIHlvdSBhcmUgZ29pbmcgdG8gb25seSBkbyB0aGUgYW5pbWF0aW9uIGZvciB0aGUgdGhlIDE1dGggb2YgZWFjaCBtb250aC4gU28sIGZpbHRlciBvbmx5IHRvIHRob3NlIGRhdGVzIC0gdGhlcmUgYXJlIHNvbWUgbHVicmlkYXRlIGZ1bmN0aW9ucyB0aGF0IGNhbiBoZWxwIHlvdSBkbyB0aGlzLiAgIAogICogVXNlIHRoZSBgYW5pbWF0ZSgpYCBmdW5jdGlvbiB0byBtYWtlIHRoZSBhbmltYXRpb24gMjAwIGZyYW1lcyBpbnN0ZWFkIG9mIHRoZSBkZWZhdWx0IDEwMCBhbmQgdG8gcGF1c2UgZm9yIDEwIGZyYW1lcyBvbiB0aGUgZW5kIGZyYW1lLiAgIAogICogVXNlIGBncm91cCA9IGRhdGVgIGluIGBhZXMoKWAuICAgCiAgKiBDb21tZW50IG9uIHdoYXQgeW91IHNlZS4gIAoKYGBge3J9CmNlbnN1c19wb3BfZXN0XzIwMTggPC0gcmVhZF9jc3YoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvNnR4d3YzYjRuZzdwZXBlL3VzX2NlbnN1c18yMDE4X3N0YXRlX3BvcF9lc3QuY3N2P2RsPTEiKSAlPiUgCiAgc2VwYXJhdGUoc3RhdGUsIGludG8gPSBjKCJkb3QiLCJzdGF0ZSIpLCBleHRyYSA9ICJtZXJnZSIpICU+JSAKICBzZWxlY3QoLWRvdCkgJT4lIAogIG11dGF0ZShzdGF0ZSA9IHN0cl90b19sb3dlcihzdGF0ZSkpCgpzdGF0ZXNfbWFwIDwtIG1hcF9kYXRhKCJzdGF0ZSIpCgpuZXdlcl9jYXNlcyA8LSBjb3ZpZDE5ICU+JSAKICBhcnJhbmdlKGRlc2MoZGF0ZSkpICU+JSAKICBncm91cF9ieShzdGF0ZSkgJT4lIAogIG11dGF0ZShudW1iZXJyb3cgPSAxOm4oKSkgJT4lIAogIG11dGF0ZShzdGF0ZSA9IHN0cl90b19sb3dlcihgc3RhdGVgKSkKCmNvdmlkX3BvcHVsYXRpb24gPC0gbmV3ZXJfY2FzZXMgJT4lIAogIGxlZnRfam9pbiggY2Vuc3VzX3BvcF9lc3RfMjAxOCwKICAgICAgICAgICAgIGJ5ID0gYygic3RhdGUiPSJzdGF0ZSIpKSAlPiUgCiAgbXV0YXRlKGNvdmlkX3Blcl8xMDAwMCA9IChjYXNlcy9lc3RfcG9wXzIwMTgpKjEwMDAwKQoKY292aWRfcG9wdWxhdGlvbiAlPiUgCiAgZmlsdGVyKGRheShkYXRlKSA9PSAxNSkgJT4lIAogIGdncGxvdCgpKwogIGdlb21fbWFwKG1hcCA9IHN0YXRlc19tYXAsCiAgICAgICAgICAgYWVzKG1hcF9pZCA9IHN0YXRlLAogICAgICAgICAgICAgICBmaWxsID0gY292aWRfcGVyXzEwMDAwLAogICAgICAgICAgICAgICBncm91cCA9IGRhdGUpKSsKICBsYWJzKHRpdGxlID0gIkNPVklELTE5IGNhc2VzIHBlciAxMCwwMDAgcmVzaWRlbnRzIGJ5IGRhdGUgaW4gdGhlIFVTIiwKICAgICAgIHN1YnRpdGxlID0gIkRhdGU6IHtjbG9zZXN0X3N0YXRlfSIsCiAgICAgICBmaWxsID0gIkNhc2VzIHBlciAxMCwwMDAiLAogICAgICAgY2FwdGlvbiA9ICIiKSsKICBleHBhbmRfbGltaXRzKCB4ID0gc3RhdGVzX21hcCRsb25nLCB5ID0gc3RhdGVzX21hcCRsYXQpKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCBvcHRpb24gPSAibWFnbWEiLCBkaXJlY3Rpb24gPSAtMSkrCiAgdGhlbWVfbWFwKCkrCiAgdGhlbWUobGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpKwogIHRyYW5zaXRpb25fc3RhdGVzKGRhdGUsIHRyYW5zaXRpb25fbGVuZ3RoID0gMCkKCmFuaW1fc2F2ZSgiY292aWRfY2FzZXMuZ2lmIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImNvdmlkX2Nhc2VzLmdpZiIpCgpgYGAKCiMjIFlvdXIgZmlyc3QgYHNoaW55YCBhcHAgKGZvciBuZXh0IHdlZWshKQoKICA3LiBUaGlzIGFwcCB3aWxsIGFsc28gdXNlIHRoZSBDT1ZJRCBkYXRhLiBNYWtlIHN1cmUgeW91IGxvYWQgdGhhdCBkYXRhIGFuZCBhbGwgdGhlIGxpYnJhcmllcyB5b3UgbmVlZCBpbiB0aGUgYGFwcC5SYCBmaWxlIHlvdSBjcmVhdGUuIFlvdSBzaG91bGQgY3JlYXRlIGEgbmV3IHByb2plY3QgZm9yIHRoZSBhcHAsIHNlcGFyYXRlIGZyb20gdGhlIGhvbWV3b3JrIHByb2plY3QuIEJlbG93LCB5b3Ugd2lsbCBwb3N0IGEgbGluayB0byB0aGUgYXBwIHRoYXQgeW91IHB1Ymxpc2ggb24gc2hpbnlhcHBzLmlvLiBZb3Ugd2lsbCBjcmVhdGUgYW4gYXBwIHRvIGNvbXBhcmUgc3RhdGVzJyBkYWlseSBudW1iZXIgb2YgQ09WSUQgY2FzZXMgcGVyIDEwMCwwMDAgb3ZlciB0aW1lLiBUaGUgeC1heGlzIHdpbGwgYmUgZGF0ZS4gWW91IHdpbGwgaGF2ZSBhbiBpbnB1dCBib3ggd2hlcmUgdGhlIHVzZXIgY2FuIGNob29zZSB3aGljaCBzdGF0ZXMgdG8gY29tcGFyZSAoYHNlbGVjdElucHV0KClgKSwgYSBzbGlkZXIgd2hlcmUgdGhlIHVzZXIgY2FuIGNob29zZSB0aGUgZGF0ZSByYW5nZSwgYW5kIGEgc3VibWl0IGJ1dHRvbiB0byBjbGljayBvbmNlIHRoZSB1c2VyIGhhcyBjaG9zZW4gYWxsIHN0YXRlcyB0aGV5J3JlIGludGVyZXN0ZWQgaW4gY29tcGFyaW5nLiBUaGUgZ3JhcGggc2hvdWxkIGRpc3BsYXkgYSBkaWZmZXJlbnQgbGluZSBmb3IgZWFjaCBzdGF0ZSwgd2l0aCBsYWJlbHMgZWl0aGVyIG9uIHRoZSBncmFwaCBvciBpbiBhIGxlZ2VuZC4gQ29sb3IgY2FuIGJlIHVzZWQgaWYgbmVlZGVkLiAKICAKIApQdXQgdGhlIGxpbmsgdG8geW91ciBhcHAgaGVyZTogaHR0cHM6Ly9lZGVsZW9uby5zaGlueWFwcHMuaW8vY292aWQtYXBwLwogIAojIyBHaXRIdWIgbGluazogaHR0cHM6Ly9naXRodWIuY29tL3Jvc2RlL2NvdmlkLWFwcAoKICA4LiBCZWxvdywgcHJvdmlkZSBhIGxpbmsgdG8geW91ciBHaXRIdWIgcmVwbyB3aXRoIHRoaXMgc2V0IG9mIFdlZWtseSBFeGVyY2lzZXMuIAogIApodHRwczovL2dpdGh1Yi5jb20vcm9zZGUvZXhlcmNpc2U1IAoKKipESUQgWU9VIFJFTUVNQkVSIFRPIFVOQ09NTUVOVCBUSEUgT1BUSU9OUyBBVCBUSEUgVE9QPyoqCg==